using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using Digitalmes.Application.Sys.Users.Queries;
using Digitalmes.WebApi.Infrastructure.Jwt;

namespace Digitalmes.WebApi.Endpoints;

/// <summary>
/// 用户验证终结点。
/// </summary>
public sealed class AuthEndpoint : EndpointGroupBase
{
    public override void Map(WebApplication app)
    {
        app.MapGroup(this)
            .RequireAuthorization()
            .MapGet(GetAccessCodes, "codes")
            .MapPost(LoginAsync, "login")
            .MapPost(RefreshToken, "refresh-token")
            .MapPost(Logout, "logout");
    }

    [AllowAnonymous]
    [EndpointDescription("账户密码登录")]
    public async Task<IApiResult> LoginAsync(LoginViewModel model, ISender sender, IJwtAuthManager jwtAuthManager, TimeProvider time, IOptions<JwtTokenConfig> options)
    {
        var (ok, user) = await sender.Send(new ValidateAndGetUserQuery(model.Username, model.Password));
        if (!ok)
        {
            return ApiResult.Bad("账号或密码错误");
        }

        JwtToken token = new()
        {
            UserId = user.Id,
            Username = user.Username,
            Roles = user.UserRoles?
                .Where(s => s.Role?.Status == EnabledStatusEnum.Enabled)
                .Select(s => s.Role!.Code)
                .ToArray() ?? [],
        };

        var now = time.GetLocalNow().DateTime;
        var accessTokenExp = TimeSpan.FromMinutes(options.Value.AccessTokenExpiration);
        var refreshTokenExp = TimeSpan.FromMinutes(options.Value.RefreshTokenExpiration);

        var resp = new
        {
            accessToken = jwtAuthManager.GenerateToken(token, now, accessTokenExp),
            refreshToken = jwtAuthManager.GenerateToken(token, now, refreshTokenExp),
            expires = now.Add(accessTokenExp),
        };

        return ApiResult.Ok(resp);
    }

    [AllowAnonymous]
    [EndpointDescription("获取刷新Token（以旧换新）")]
    public IApiResult RefreshToken(RefreshTokenViewModel model, IJwtAuthManager jwtAuthManager, TimeProvider time, IOptions<JwtTokenConfig> options)
    {
        var now = time.GetLocalNow().DateTime;
        var accessTokenExp = TimeSpan.FromMinutes(options.Value.AccessTokenExpiration);
        var refreshTokenExp = TimeSpan.FromMinutes(options.Value.RefreshTokenExpiration);

        // 思考：
        // refreshToken 与 accessToken 应该区分开，refreshToken 只能适用于刷新功接口，且调用接口即不再可用。

        try
        {
            var resp = new
            {
                accessToken = jwtAuthManager.Refresh(model.RefreshToken, now, accessTokenExp),
                refreshToken = jwtAuthManager.Refresh(model.RefreshToken, now, refreshTokenExp),
                expires = now.Add(accessTokenExp),
            };
            return ApiResult.Ok(resp);
        }
        catch (SecurityTokenException)
        {
            return ApiResult.Bad(4401, "认证已过期，需要重新登录");
        }
    }

    [EndpointDescription("获取用户访问权限码")]
    public IApiResult GetAccessCodes(ICurrentUser currentUser)
    {
        // 根据菜单按钮选项
        return ApiResult.Ok(Array.Empty<string>());
    }

    [AllowAnonymous]
    [EndpointDescription("退出登录")]
    public IApiResult Logout()
    {
        return ApiResult.Ok();
    }
}
